package com.netdisk.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.netdisk.dto.FileDTO;
import com.netdisk.entity.File;
import com.netdisk.entity.Trash;
import com.netdisk.entity.User;
import com.netdisk.exception.BusinessException;
import com.netdisk.mapper.FileMapper;
import com.netdisk.service.FileService;
import com.netdisk.service.TrashService;
import com.netdisk.service.UserService;
import com.netdisk.vo.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件服务实现类
 */
@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {

    @Value("${file.upload-dir}")
    private String uploadDir;

    @Autowired
    private UserService userService;

    @Autowired
    private TrashService trashService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileDTO uploadFile(MultipartFile file, Integer userId) {
        // 获取用户信息
        User user = userService.getById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 检查存储空间是否足够
        if (user.getStorageUsed() + file.getSize() > user.getStorageQuota()) {
            throw new BusinessException("存储空间不足");
        }

        // 生成唯一文件名
        String originalFilename = file.getOriginalFilename();
        
        // 确保获取到文件名
        if (originalFilename == null || originalFilename.trim().isEmpty()) {
            log.warn("上传的文件没有原始文件名，使用默认名称");
            originalFilename = "unknown_file";
        }
        
        // 获取后缀，处理特殊情况
        String suffix = FileUtil.getSuffix(originalFilename);
        log.info("FileUtil.getSuffix 返回的后缀: {}", suffix);
        
        // 如果Hutool的getSuffix返回空或没有正确识别，手动提取后缀
        if (StrUtil.isBlank(suffix)) {
            int lastDotIndex = originalFilename.lastIndexOf('.');
            if (lastDotIndex > 0 && lastDotIndex < originalFilename.length() - 1) {
                suffix = originalFilename.substring(lastDotIndex + 1);
                log.info("手动提取的后缀: {}", suffix);
            }
        }
        
        String uniqueFileName = IdUtil.fastSimpleUUID() + "." + suffix;
        
        // 确定文件类型
        String fileType = determineFileType(suffix);
        
        // 添加调试日志
        log.info("上传文件: originalFilename={}, suffix={}, fileType={}", originalFilename, suffix, fileType);
        
        // 保存文件
        String relativePath = userId + "/" + uniqueFileName;
        String absolutePath = uploadDir + "/" + relativePath;
        
        try {
            // 确保目录存在
            FileUtil.mkdir(FileUtil.file(uploadDir, String.valueOf(userId)));
            
            // 保存文件
            java.io.File destFile = new java.io.File(absolutePath);
            file.transferTo(destFile);
            
            // 保存文件信息到数据库
            File fileEntity = new File();
            fileEntity.setName(originalFilename);
            fileEntity.setSize(file.getSize());
            fileEntity.setType(fileType);
            fileEntity.setPath(relativePath);
            fileEntity.setUserId(userId);
            fileEntity.setIsFavorite(false);
            this.save(fileEntity);
            
            // 更新用户已使用存储空间
            userService.updateStorageUsed(userId, file.getSize());
            
            // 返回文件信息
            FileDTO fileDTO = new FileDTO();
            BeanUtils.copyProperties(fileEntity, fileDTO);
            return fileDTO;
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new BusinessException("文件上传失败");
        }
    }

    @Override
    public FileDTO getFileInfo(Integer fileId, Integer userId) {
        // 查询文件信息
        LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(File::getId, fileId)
                .eq(File::getUserId, userId)
                .isNull(File::getDeletedAt);
        File file = this.getOne(queryWrapper);
        if (file == null) {
            throw new BusinessException("文件不存在");
        }
        
        // 返回文件信息
        FileDTO fileDTO = new FileDTO();
        BeanUtils.copyProperties(file, fileDTO);
        return fileDTO;
    }

    @Override
    public PageResult<FileDTO> listUserFiles(Integer userId, Integer page, Integer pageSize, String query) {
        System.out.println("Listing files for user: " + userId + ", page: " + page + ", pageSize: " + pageSize + ", query: " + query);
        
        try {
            // 计算分页参数
            int offset = (page - 1) * pageSize;
            
            // 使用直接SQL查询，避开MyBatis-Plus的逻辑删除机制
            List<File> records = baseMapper.selectUserFilesWithQuery(userId, pageSize, offset, query);
            System.out.println("Direct SQL returned " + records.size() + " files");
            
            // 获取总记录数 - 使用直接SQL查询
            int total = baseMapper.countUserNonDeletedFilesWithQuery(userId, query);
            System.out.println("Total count: " + total);
            
            // 再次检查结果中是否有已删除的文件
            if (records != null && !records.isEmpty()) {
                int beforeSize = records.size();
                
                // 过滤掉已删除的文件（虽然SQL查询已经排除，但以防万一）
                records = records.stream()
                        .filter(file -> file.getDeletedAt() == null)
                        .collect(Collectors.toList());
                
                int afterSize = records.size();
                if (afterSize < beforeSize) {
                    System.out.println("WARNING: Filtered out " + (beforeSize - afterSize) + " deleted files!");
                }
            }
            
            // 转换为DTO
            List<FileDTO> fileDTOList = records.stream().map(file -> {
                FileDTO fileDTO = new FileDTO();
                BeanUtils.copyProperties(file, fileDTO);
                return fileDTO;
            }).collect(Collectors.toList());
            
            System.out.println("Returning " + fileDTOList.size() + " files as DTOs");
            
            // 返回分页结果
            return new PageResult<>((long) total, fileDTOList);
        } catch (Exception e) {
            System.err.println("Error in listUserFiles: " + e.getMessage());
            e.printStackTrace();
            return new PageResult<>(0L, new ArrayList<>());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteFile(Integer fileId, Integer userId) {
        try {
            System.out.println("Moving file to trash: fileId=" + fileId + ", userId=" + userId);
            
            // 查询文件信息 - 避免使用isNull条件
            File file = this.getById(fileId);
            if (file == null) {
                System.out.println("File not found: fileId=" + fileId);
                throw new BusinessException("文件不存在");
            }
            
            if (!file.getUserId().equals(userId)) {
                System.out.println("File does not belong to user: fileId=" + fileId + ", userId=" + userId);
                throw new BusinessException("您无权操作此文件");
            }
            
            if (file.getDeletedAt() != null) {
                System.out.println("File already in trash: fileId=" + fileId);
                throw new BusinessException("文件已在回收站中");
            }
            
            System.out.println("File found: " + file);
            
            // 直接更新文件对象
            file.setDeletedAt(LocalDateTime.now());
            boolean result = this.updateById(file);
            
            if (result) {
                System.out.println("File marked as deleted: fileId=" + fileId);
                
                // 检查是否已在回收站
                LambdaQueryWrapper<Trash> trashQueryWrapper = new LambdaQueryWrapper<>();
                trashQueryWrapper.eq(Trash::getFileId, fileId);
                if (trashService.count(trashQueryWrapper) == 0) {
                    // 添加到回收站
                    Trash trash = new Trash();
                    trash.setFileId(fileId);
                    trash.setUserId(userId);
                    // 显式设置删除时间
                    trash.setDeletedAt(LocalDateTime.now());
                    boolean trashSaved = trashService.save(trash);
                    System.out.println("Added to trash: " + trashSaved);
                } else {
                    System.out.println("File already in trash table, no need to add");
                }
            } else {
                System.out.println("Failed to update file status: fileId=" + fileId);
            }
            
            return result;
        } catch (BusinessException be) {
            throw be;
        } catch (Exception e) {
            System.err.println("Error moving file to trash: " + e.getMessage());
            e.printStackTrace();
            throw new BusinessException("移动到回收站失败: " + e.getMessage());
        }
    }

    @Override
    public FileDTO renameFile(Integer fileId, String newName, Integer userId) {
        // 查询文件信息
        LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(File::getId, fileId)
                .eq(File::getUserId, userId)
                .isNull(File::getDeletedAt);
        File file = this.getOne(queryWrapper);
        if (file == null) {
            throw new BusinessException("文件不存在");
        }
        
        // 更新文件名
        file.setName(newName);
        this.updateById(file);
        
        // 返回文件信息
        FileDTO fileDTO = new FileDTO();
        BeanUtils.copyProperties(file, fileDTO);
        return fileDTO;
    }

    @Override
    public String downloadFile(Integer fileId, Integer userId) {
        // 查询文件信息
        LambdaQueryWrapper<File> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(File::getId, fileId)
                .eq(File::getUserId, userId)
                .isNull(File::getDeletedAt);
        File file = this.getOne(queryWrapper);
        if (file == null) {
            throw new BusinessException("文件不存在");
        }
        
        // 返回文件路径
        return uploadDir + "/" + file.getPath();
    }
    
    /**
     * 根据文件后缀确定文件类型
     *
     * @param suffix 文件后缀
     * @return 文件类型
     */
    private String determineFileType(String suffix) {
        if (StrUtil.isBlank(suffix)) {
            log.warn("文件后缀为空，归类为other");
            return "other";
        }
        
        // 确保后缀为小写且不包含点号
        String originalSuffix = suffix;
        suffix = suffix.toLowerCase().trim();
        if (suffix.startsWith(".")) {
            suffix = suffix.substring(1);
        }
        
        log.info("处理文件类型: originalSuffix={}, normalizedSuffix={}", originalSuffix, suffix);
        
        // 文档类型
        if (StrUtil.equalsAny(suffix, "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", "txt", "md", 
                "rtf", "odt", "ods", "odp", "csv", "epub", "mobi", "tex", "pages", "numbers", "key")) {
            log.info("文件类型: document, suffix={}", suffix);
            return "document";
        }
        
        // 图片类型
        if (StrUtil.equalsAny(suffix, "jpg", "jpeg", "png", "gif", "bmp", "svg", "webp", "tiff", "ico", "heic", "raw")) {
            log.info("文件类型: image, suffix={}", suffix);
            return "image";
        }
        
        // 视频类型
        if (StrUtil.equalsAny(suffix, "mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "mpeg", "m4v", "3gp", "rmvb")) {
            log.info("文件类型: video, suffix={}", suffix);
            return "video";
        }
        
        // 音频类型
        if (StrUtil.equalsAny(suffix, "mp3", "wav", "ogg", "flac", "aac", "m4a", "wma", "opus", "midi", "amr")) {
            log.info("文件类型: audio, suffix={}", suffix);
            return "audio";
        }
        
        // 压缩文件类型
        if (StrUtil.equalsAny(suffix, "zip", "rar", "7z", "tar", "gz", "bz2", "xz", "iso", "dmg", "tgz", "lz")) {
            log.info("文件类型: archive, suffix={}", suffix);
            return "archive";
        }
        
        // 代码文件类型
        if (StrUtil.equalsAny(suffix, "js", "ts", "html", "css", "php", "py", "java", "c", "cpp", "cs", "json", "xml",
                "go", "rb", "swift", "sql", "sh", "bat", "ps1", "yaml", "yml", "vue", "jsx", "tsx")) {
            log.info("文件类型: code, suffix={}", suffix);
            return "code";
        }
        
        log.info("未识别的文件类型: {}, 归类为other", suffix);
        return "other";
    }
} 